数组

xmind-03

数组是什么

数组:(Array) 是一种可以按顺序保存数据的数据类型,可以存储多个元素。

使用场景: 如果有多个数据可以用数组保存起来,然后放到一个变量中,管理非常方便

数组的基本使用

定义数组和数组单元

let 数组名 = [] // 空数组
let 数组名 = [数据1, 数据2, ..., 数据n] // 包含 n 个元素的数组

let 数组名 = new Array(); // 无参构造函数
let 数组名 = new Array(n); // 带有一个参数的构造函数,创建指定长度 n 的空数组
let 数组名 = new Array(数据1, 数据2, ..., 数据n) // 带有多个参数的构造函数,创建包含这些元素的数组
var myArray = []; // 空数组
var myArray = [1, 2, 3]; // 包含 3 个元素的数组
var myArray = new Array(); // 空数组,使用构造函数方式
var myArray = new Array(1, 2, 3); // 包含 3 个元素的数组,使用构造函数方式

访问数组和数组索引

let names = ["小明", "小刚", "小红", "小丽", "小米"];
// 索引值       0	     1      2       3       4

数据单元【小明】对应的索引值为【0】,数据单元【小红】对应的索引值为【2】

let myArray = ["apple", "banana", "orange"];

console.log(myArray[0]); // 输出"apple"
console.log(myArray[2]); // 输出"orange"
console.log(myArray[3]); // 输出 undefined,因为这个数组只有 3 个元素

修改数组中的元素

可以使用数组索引来修改数组中的元素。

let myArray = ["apple", "banana", "orange"];

myArray[1] = "grape";

console.log(myArray); // 输出 ["apple", "grape", "orange"]

数据单元值类型

数组做为数据的集合,它的单元值可以是任意数据类型。

// 数组单值类型可以是任意数据类型

// a) 数组单元值的类型为字符类型
let list = ["HTML", "CSS", "JavaScript"];
// b) 数组单元值的类型为数值类型
let scores = [78, 84, 70, 62, 75];
// c) 混合多种类型
let mixin = [true, 1, false, "hello"];

数组长度属性

重申一次,数组在 JavaScript 中并不是新的数据类型,它属于对象类型。

// 定义一个数组
let arr = ["html", "css", "javascript"];
// 数组对应着一个 length 属性,它的含义是获取数组的长度
console.log(arr.length); // 3

let emptyArray = [];
console.log(emptyArray.length); // 输出:0

let myArray = [1, 2, 3, 4, 5];
console.log(myArray[0]); // 输出 1
console.log(myArray[3]); // 输出 4
console.log(myArray[myArray.length - 1]); // 输出 5,数组最后一个元素的索引是数组长度 -1
注意

数组的长度并不一定等于最后一个元素的索引加 1。

let arr = [1, 2, 3];
arr[10] = 4;
console.log(arr.length); // 输出:11

数组 arr 的长度是 11,尽管它只有四个实际设置的元素。这是因为 JavaScript 会根据数组中最大的索引值自动调整其长度。

遍历数组

遍历数组是指逐一访问数组中的每个元素并执行相应的操作。一般会用 for 循环遍历。

for 循环

使用 for 循环可以依次访问数组中的每个元素,并对其进行操作。可以使用数组的 length 属性作为循环条件,然后在循环体内访问每个元素。

let arr = ["apple", "banana", "orange"];
for (let i = 0; i < arr.length; i++) {
  console.log(arr[i]);
}
// 输出:apple, banana, orange

forEach 方法

使用 forEach 方法可以对数组中的每个元素执行一个回调函数。

let arr = ["apple", "banana", "orange"];
// arr.forEach(function(item) {
arr.forEach((item) => {
  console.log(item);
});
// 输出:apple, banana, orange

map 方法

map() 方法可以创建一个新的数组,其中包含对原始数组中的每个元素应用某个函数后的结果。它与 forEach() 方法类似,但不同之处在于它返回一个新的数组。

let arr = [1, 2, 3];
// let mappedArr = arr.map(function(item) {
let doubledNumbers = arr.map((item) => {
  return item * 2;
});
console.log(mappedArr); // 输出:[2, 4, 6]

filter 方法

使用 filter 方法可以根据某个条件过滤出数组中符合条件的元素并返回一个新的数组。

var array = [1, 2, 3, 4, 5];

var filteredArray = array.filter(function (element) {
  return element % 2 === 0;
});

console.log(filteredArray); // Output: [2, 4]

reduce 方法

使用 reduce 方法可以将数组中的每个元素依次累加,并返回一个最终的结果。

var array = [1, 2, 3, 4, 5];

var sum = array.reduce(function (accumulator, currentValue) {
  return accumulator + currentValue;
});

console.log(sum); // Output: 15

for…in 循环

for…in 循环可以遍历对象的属性,但也可以用来遍历数组。它会枚举数组中的所有索引,而不仅仅是数字索引。

let arr = [3, 5, 7];
arr.foo = "hello";
for (let i in arr) {
  console.log(i); // logs "0", "1", "2", "foo"
}

let arr = ["apple", "banana", "orange"];
for (let index in arr) {
  console.log(arr[index]);
}
// 输出:apple, banana, orange

for…of 循环

for…of 语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句

const array1 = ["a", "b", "c"];

for (const element of array1) {
  console.log(element);
}

// Expected output: "a"
// Expected output: "b"
// Expected output: "c"

操作数组

数组本质是数据集合,操作数据无非就是 增 删 改 查 语法:

添加数组元素

  1. 想要数组末尾增加数据元素利用那个方法?
    • arr.push()
    • 可以添加一个或者多个数组元素
    • 返回的是数组长度
  2. 想要数组开头增加数据元素利用那个方法?
    • arr.unshift()
    • 可以添加一个或者多个数组元素
    • 返回的是数组长度
  3. 重点记住那个?
    • arr.push()
const arr = [1, 2, 3];
arr.push(4);
console.log(arr); // [1, 2, 3, 4]

arr.unshift(0);
console.log(arr); // [0, 1, 2, 3, 4]

arr.splice(2, 0, 5, 6);
console.log(arr); // [0, 1, 5, 6, 2, 3, 4]

push 方法

push() 方法可以向数组末尾添加一个或多个元素,并返回新数组的长度。它会修改原数组。

array.push(element1, element2, ..., elementN)
// element1, element2, ..., elementN:要添加到数组末尾的元素。

// 向数组末尾添加一个元素
let arr = [1, 2];
arr.push(3);
console.log(arr); // 输出 [1, 2, 3]

// 向数组末尾同时添加多个元素
let arr = [1, 2];
arr.push(3, 4, 5);
console.log(arr); // 输出 [1, 2, 3, 4, 5]

unshift 方法

unshift() 方法可以向数组开头添加一个或多个元素,并返回新数组的长度。它会修改原数组。

array.unshift(element1, element2, ..., elementN)
// element1, element2, ..., elementN:要添加到数组开头的元素。

// 向数组开头添加一个元素
let arr = [1, 2];
arr.unshift(0);
console.log(arr); // 输出 [0, 1, 2]

// 向数组开头同时添加多个元素
let arr = [1, 2];
arr.unshift(-2, -1, 0);
console.log(arr); // 输出 [-2, -1, 0, 1, 2]

splice 方法

splice() 方法可以在任意位置添加或删除一个或多个元素,并返回被删除元素组成的数组。它会修改原数组。

array.splice(index, howMany, element1, ..., elementN)
// index:要添加或删除元素的位置。
// howMany:要删除的元素个数。如果为 0,则不删除任何元素。
// element1, ..., elementN:要添加到数组中的元素。

// 向数组开头添加元素
let arr = [1, 2];
arr.splice(0, 0, 0);
console.log(arr); // 输出 [0, 1, 2]

// 在指定位置插入多个元素
let arr = [1, 2];
arr.splice(1, 0, -1, 0);
console.log(arr); // 输出 [1, -1, 0, 2]

concat 方法

concat() 方法可以将多个数组合并成一个新的数组,而不会修改原数组。

array.concat(array1, array2, ..., arrayN)
// array1, array2, ..., arrayN:要合并的数组。

// 将多个数组合并成
let arr1 = [1, 2];
let arr2 = [3, 4, 5];
let newArr = arr1.concat(arr2);
console.log(newArr); // 输出 [1, 2, 3, 4, 5]

// 直接添加元素
let arr1 = [1, 2];
let newArr = arr1.concat(3, 4, 5);
console.log(newArr); // 输出 [1, 2, 3, 4, 5]

注意

concat() 方法不会修改原数组,而是返回一个新的数组。

删除数组元素

  1. 想要数组末尾删除 1 个数据元素利用那个方法?带参数吗?
    • arr.pop()
    • 不带参数
    • 返回值是删除的元素
  2. 想要数组开头删除 1 个数据元素利用那个方法?带参数吗?
    • arr.shift()
    • 不带参数
    • 返回值是删除的元素
  3. 想要指定删除数组元素用那个?开发常用吗?有那些使用场景?
    • arr.splice(起始位置,删除的个数)
    • 开发很常用,比如随机抽奖,比如删除指定商品等等
注意

splice()shift()pop() 方法都可以修改原始数组,并返回已被删除的元素。如果不想修改原始数组,可以先复制一个新的数组,然后对其进行操作。

使用场景:

  1. 随机抽奖,中奖的用户就需要从数组里面删除,不允许重复抽奖
  2. 点击删除按钮,相关的数据会从商品数据中删除
const arr = [0, 1, 2, 3, 4];
arr.pop();
console.log(arr); // [0, 1, 2, 3]

arr.shift();
console.log(arr); // [1, 2, 3]

arr.splice(1, 2);
console.log(arr); // [1]

splice 方法

splice() 方法可以通过指定起始索引和要删除的元素数量来删除数组中的元素。除了删除元素之外,它还可以在指定位置添加新的元素。

array.splice(start, deleteCount, item1, item2, ...)
// start:必需。规定要删除的元素的起始位置(从 0 开始)。如果该值为负数,则表示从末尾开始计算。
// deleteCount:可选。规定要删除的元素数量。如果省略该参数,则删除从起始位置开始到数组末尾的所有元素。
// item1, item2, ...:可选。要添加到数组的新元素,从 start 位置开始。

// 返回值:被删除的元素组成的数组。如果没有删除任何元素,则返回空数组。

// 从指定索引处删除一个元素:
let arr = ['A', 'B', 'C', 'D'];
arr.splice(2, 1); // 从索引 2 删除 1 个元素
console.log(arr); // ["A", "B", "D"]

// 从开头删除一个元素
let arr = ['A', 'B', 'C', 'D'];
arr.splice(0, 1); // 从索引 0 删除 1 个元素
console.log(arr); // ["B", "C", "D"]

// 从末尾删除一个元素
let arr = ['A', 'B', 'C', 'D'];
arr.splice(-1, 1); // 从最后一个索引删除 1 个元素
console.log(arr); // ["A", "B", "C"]

// 将数组 ['A', 'B', 'C', 'D'] 的第二个和第三个元素 'B' 和 'C' 删除,并在其位置插入两个新元素 'E' 和 'F',最终得到新的数组 ['A', 'E', 'F', 'D']。
let arr = ['A', 'B', 'C', 'D'];
arr.splice(1, 2, 'E', 'F'); // 从索引 1 开始,删除 2 个元素,然后插入 'E' 和 'F'
console.log(arr); // ["A", "E", "F", "D"]

shift 方法

shift() 方法会删除数组的第一个元素,并返回已被删除的元素。

array.shift();
// 删除数组的第一个元素,并返回已被删除的元素

// 将数组 ['A', 'B', 'C', 'D'] 的第一个元素 'A' 删除,并将其赋值给变量 first,最终得到新的数组 ['B', 'C', 'D']。
let arr = ["A", "B", "C", "D"];
let first = arr.shift(); // 删除第一个元素 'A',并将其赋值给变量 first
console.log(arr); // ["B", "C", "D"]
console.log(first); // "A"

pop 方法

pop() 方法会删除数组的最后一个元素,并返回已被删除的元素。

array.pop();
// 删除数组的最后一个元素,并返回已被删除的元素

// 将数组 ['A', 'B', 'C', 'D'] 的最后一个元素 'D' 删除,并将其赋值给变量 last,最终得到新的数组 ['A', 'B', 'C']。
let arr = ["A", "B", "C", "D"];
let last = arr.pop(); // 删除最后一个元素 'D',并将其赋值给变量 last
console.log(arr); // ["A", "B", "C"]
console.log(last); // "D"

delete 操作符

delete 操作符:该操作符可以删除数组中指定位置的元素,但是它会留下一个 undefined 值的空位,而不是调整数组的大小。

delete array[index];
// 删除索引 index 的元素

let myArray = [1, 2, 3, 4];
delete myArray[1]; // 删除索引 1 的元素(即 2)
console.log(myArray); // [1, undefined, 3, 4]

数组查找

JavaScript 中常用的数组查找方法有:

  1. Array.prototype.indexOf():返回指定元素在数组中第一次出现的位置。
  2. Array.prototype.lastIndexOf():返回指定元素在数组中最后一次出现的位置。
  3. Array.prototype.includes():判断数组中是否包含指定元素。
  4. Array.prototype.find():返回满足条件的第一个元素。
  5. Array.prototype.findIndex():返回满足条件的第一个元素的索引。

indexOf 方法

indexOf() 方法返回指定元素在数组中第一次出现的位置。如果该元素不在数组中,则返回 -1。

array.indexOf(searchElement[, fromIndex])
// searchElement:要查找的元素。
// fromIndex(可选):从该位置开始搜索元素。默认为 0,即从数组开头开始搜索。
// 返回值:指定元素在数组中第一次出现的位置,如果不存在则返回 -1。

const numbers = [1, 2, 3, 4, 5];
console.log(numbers.indexOf(3)); // 2
console.log(numbers.indexOf(6)); // -1

const fruits = ['apple', 'banana', 'orange'];
console.log(fruits.indexOf('banana')); // 输出 1
console.log(fruits.indexOf('mango')); // 输出 -1

lastIndexOf 方法

lastIndexOf() 方法返回指定元素在数组中最后一次出现的位置。如果该元素不在数组中,则返回 -1。

array.lastIndexOf(searchElement[, fromIndex])
// searchElement:要查找的元素。
// fromIndex(可选):从该位置开始搜索元素。默认为数组末尾,即从数组结尾开始搜索。
// 返回值:指定元素在数组中最后一次出现的位置,如果不存在则返回 -1。

const numbers = [1, 2, 3, 4, 5, 3];
console.log(numbers.lastIndexOf(3)); // 5
console.log(numbers.lastIndexOf(6)); // -1

includes 方法

includes() 方法用于判断数组中是否包含指定元素。

array.includes(searchElement[, fromIndex])
// searchElement:要查找的元素。
// fromIndex(可选):从该位置开始搜索元素。默认为 0,即从数组开头开始搜索。
// 返回值:如果指定元素存在于数组中,则返回 true;否则返回 false。

const numbers = [1, 2, 3, 4, 5];
console.log(numbers.includes(3)); // true
console.log(numbers.includes(6)); // false

const fruits = ['apple', 'banana', 'orange'];
console.log(fruits.indexOf('banana')); // 输出 1
console.log(fruits.indexOf('mango')); // 输出 -1

find 方法

find() 方法返回满足条件的第一个元素,如果没有找到则返回 undefined

array.find(callback[, thisArg])
// callback:用于测试每个元素的函数。该函数接受三个参数:当前元素、当前索引和数组本身。
// thisArg(可选):执行回调时使用的 this 值。
// 返回值:返回满足条件的第一个元素,如果没有找到则返回 undefined。

const numbers = [1, 2, 3, 4, 5];
const result = numbers.find(function(element) {
   return element > 2;
});
console.log(result); // 3

const numbers = [1, 2, 3, 4, 5];
const foundNumber = numbers.find(number => number > 3);
console.log(foundNumber); // 输出 4

findIndex 方法

findIndex() 方法返回满足条件的第一个元素的索引,如果没有找到则返回 -1。

array.findIndex(callback[, thisArg])
// callback:用于测试每个元素的函数。该函数接受三个参数:当前元素、当前索引和数组本身。
// thisArg(可选):执行回调时使用的 this 值。
// 返回值:返回满足条件的第一个元素的索引,如果没有找到则返回 -1。

// 从一个数组中查找第一个大于 5 的元素的索引值
const arr = [1, 6, 2, 8, 3, 9, 4, 10];
const index = arr.findIndex((item) => item > 5);
console.log(index); // 1

// 查找数字 3 的索引
const array = [1, 2, 3, 4, 5];
const index = array.findIndex((element) => element === 3);
console.log(index); // 2

filter 方法

filter() 方法可以用来查找数组中符合指定要求的所有元素,并将它们组成一个新的数组返回。filter() 方法接收一个回调函数作为参数,这个回调函数会对数组中的每个元素进行判断,并返回一个布尔值来表示该元素是否应该被保留在新的数组中。

array.filter(callback(currentValue[, index[, array]])[, thisArg])
// allback 是回调函数,其接收三个参数:
//   currentValue:当前处理的数组元素。
//   index(可选):当前处理的数组元素的索引。
//   array(可选):原数组。
// thisArg(可选):执行回调函数时使用的 this
// 回调函数中可以根据实际需求编写一些逻辑判断,只有符合条件的元素才会被添加到新的数组中。最终,filter() 方法会返回包含所有符合条件的元素的新数组。

const numbers = [1, 2, 3, 4, 5];
const filteredNumbers = numbers.filter(number => number > 3);
console.log(filteredNumbers); // 输出 [4, 5]

// 使用 filter() 方法从中查找所有大于 5 的元素,并将它们组成一个新的数组 filteredArr
// 首先定义了一个包含多个用户信息的对象数组 users,然后使用 filter() 方法从中查找所有年龄大于等于 30 岁且为女性的用户,并将它们组成一个新的数组 filteredUsers。最后,我们在控制台输出了这个新数组。
const arr = [1, 6, 2, 8, 3, 9, 4, 10];
const filteredArr = arr.filter((item) => item > 5);
console.log(filteredArr); // [6, 8, 9, 10]
// 使用 filter() 方法从一个对象数组中查找符合指定要求的所有元素
const users = [
  { name: '张三', age: 20, gender: '男' },
  { name: '李四', age: 25, gender: '女' },
  { name: '王五', age: 30, gender: '男' },
  { name: '赵六', age: 35, gender: '女' },
];
// 查找年龄大于等于 30 岁且为女性的用户
const filteredUsers = users.filter((user) => {
  return user.age >= 30 && user.gender === '女';
});
console.log(filteredUsers); // [{ name: '赵六', age: 35, gender: '女' }]

some 方法

some() 方法可以接受一个回调函数作为参数,这个回调函数会对数组中的每个元素进行判断,返回一个布尔值来表示该元素是否符合条件。如果数组中有一个或多个元素符合条件,则 some() 方法返回 true,否则返回 false

注意

some() 方法会遍历整个数组,只要找到一个符合条件的元素,就会立即停止遍历并返回 true,不再继续查找后面的元素。如果数组中没有任何一个元素符合条件,则返回 false

array.some(callback(element[, index[, array]])[, thisArg])
// callback 表示回调函数,可以接收三个参数:element、index 和 array。
//    element 表示当前遍历到的元素
//    index 表示当前元素的索引
//    array 表示原始数组对象
// thisArg 表示回调函数执行时的 this 值

// 判断数组中是否存在小于 0 的元素
const arr = [1, 2, -3, 4, 5];
const result = arr.some((item) => item < 0);
console.log(result); // true

every 方法

every() 方法与 some() 方法类似,也可以接受一个回调函数作为参数,这个回调函数会对数组中的每个元素进行判断,返回一个布尔值来表示该元素是否符合条件。不同的是,every() 方法会在遍历整个数组之后,判断是否所有的元素都符合条件,如果是则返回 true,否则返回 false

array.every(callback(element[, index[, array]])[, thisArg])
// callback 表示回调函数,可以接收三个参数:element、index 和 array。
//    element 表示当前遍历到的元素
//    index 表示当前元素的索引
//    array 表示原始数组对象
// thisArg 表示回调函数执行时的 this 值。

// 判断数组中的所有元素是否都大于 0
const arr = [1, 2, -3, 4, 5];
const result = arr.every((item) => item > 0);
console.log(result); // false
注意

every() 方法会遍历整个数组,只有当数组中的所有元素都符合条件时,才会返回 true。如果遇到一个不符合条件的元素,则立即停止遍历并返回 false。如果数组为空,则 every() 方法也会返回 true

数组排序

JavaScript 中有两种常见的排序方法:

  1. Array.prototype.sort():使用默认的排序方法对数组进行排序。
  2. 排序算法函数:可以在 sort() 方法中传递一个比较函数,自定义排序规则。
  3. 冒泡排序(Bubble Sort):通过多次交换相邻元素的位置来实现排序。时间复杂度为 O(n^2),性能较差。
  4. 插入排序(Insertion Sort):将每个元素插入到已经排序好的序列中的适当位置,最终得到一个有序序列。时间复杂度为 O(n^2),对于小型数组较为高效。
  5. 选择排序(Selection Sort):找到数组中最小的元素并将其放在第一个位置,接着从剩余未排序的元素中找到最小的元素放在第二个位置,以此类推。时间复杂度为 O(n^2),性能较差。
  6. 快速排序(Quick Sort):通过一趟排序将数组分成两个部分,其中一部分所有元素均比另一部分小,然后递归地重复上述过程直到整个数组有序。时间复杂度为 O(nlogn),是较快的排序算法之一。
  7. 归并排序(Merge Sort):将待排序数组不断地对半切分,对每个子数组进行排序,然后再将它们合并起来。时间复杂度为 O(nlogn),也是较快的排序算法之一。

sort 方法

sort() 方法使用默认的排序方法,按照 Unicode 码点进行排序。

array.sort([compareFunction]);
// compareFunction(可选):用于指定排序的规则函数。如果省略该参数,则默认按照 Unicode 码点进行排序。
// 返回值:排序后的数组(原始数组会被改变)

const fruit = ["apple", "banana", "orange", "pear"];
fruit.sort();
console.log(fruit); // ['apple', 'banana', 'orange', 'pear']

排序算法函数

排序算法函数可以在 sort() 方法中传递一个比较函数,自定义排序规则。比较函数应该返回一个负数、零或正数,具体表示如下:

array.sort(function (a, b) {
  // 比较规则
});
// a:比较的第一个元素。
// b:比较的第二个元素。
// 返回值:返回一个负数、零或正数,具体表示如上所述。

const numbers = [4, 2, 5, 1, 3];
numbers.sort(function (a, b) {
  return a - b;
});
console.log(numbers); // [1, 2, 3, 4, 5]

冒泡排序算法

冒泡排序算法是一种基本的排序算法,它通过反复交换相邻的两个元素来实现排序。具体来说,它比较相邻的两个元素大小,如果前一个元素大于后一个元素,则交换这两个元素的位置,直到最后一个元素为止。这样一轮交换之后,最大的元素就会被移动到了数组的末尾。接着,重复上述过程,除了已经排好序的元素,直到整个数组都被排序。

function bubbleSort(arr) {
  const len = arr.length;
  for (let i = 0; i < len - 1; i++) {
    for (let j = 0; j < len - i - 1; j++) {
      if (arr[j] > arr[j + 1]) {
        const temp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = temp;
      }
    }
  }
  return arr;
}

该函数接收一个数组作为参数,使用嵌套的 for 循环来进行遍历和比较操作。外层循环用于控制每一轮比较的次数,内层循环则用于比较相邻元素的大小并进行交换。最后返回排序后的数组。

冒泡排序算法的时间复杂度为 O(n^2),因此对于大型数组而言,它的性能并不优秀。然而,由于其实现简单、易于理解,在某些特定情况下依然有用武之地。

插入排序算法

插入排序算法是一种简单直观的排序方法,它通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。具体来说,插入排序算法将数组分为已排序区间和未排序区间,首先将第一个元素视为已排序区间,接着对于剩余未排序元素,依次在已排序区间中寻找其合适的插入位置,并进行移动。

function insertionSort(arr) {
  const len = arr.length;
  for (let i = 1; i < len; i++) {
    let j = i - 1;
    const temp = arr[i];
    while (j >= 0 && arr[j] > temp) {
      arr[j + 1] = arr[j];
      j--;
    }
    arr[j + 1] = temp;
  }
  return arr;
}

该函数使用 for 循环遍历每个元素,将当前要插入的元素存储到临时变量 temp 中,然后在已排序区间中找到合适的插入位置,并将已排序区间中大于该元素的元素都往后移动一个位置,最后在空出来的位置插入该元素。

插入排序算法的时间复杂度为 O(n^2),性能较差,但对于小规模的数据仍然具有较好的性能。此外,插入排序算法具有简单、稳定、原地排序等优点,适用于部分有序的数组排序。

选择排序算法

选择排序算法是一种简单直观的排序方法,它通过不断地选择未排序区间中最小的元素,并将其放到已排序区间的末尾,从而实现整个数组的排序。具体来说,选择排序算法将数组分为已排序区间和未排序区间,每次在未排序区间中找到最小的元素,并将该元素与未排序区间的第一个元素交换位置,然后将该元素视为已排序区间的最后一个元素。

function selectionSort(arr) {
  const len = arr.length;
  for (let i = 0; i < len - 1; i++) {
    let minIndex = i;
    for (let j = i + 1; j < len; j++) {
      if (arr[j] < arr[minIndex]) {
        minIndex = j;
      }
    }
    if (minIndex !== i) {
      const temp = arr[i];
      arr[i] = arr[minIndex];
      arr[minIndex] = temp;
    }
  }
  return arr;
}

该函数使用双重循环遍历每个元素,寻找未排序区间中最小的元素,并将其与未排序区间的第一个元素交换位置。外层循环用于控制每次选择的起始位置,内层循环则用于查找最小元素的位置。

选择排序算法的时间复杂度为 O(n^2),性能较差,但对于小规模的数据仍然具有较好的性能。此外,选择排序算法具有简单、稳定、原地排序等优点,适用于大多数无序数组排序。

快速排序算法

快速排序算法是一种基于分治思想的高效排序算法,它通过一趟排序将数组分成两个部分,其中一部分所有元素均比另一部分小,然后递归地重复上述过程直到整个数组有序。具体来说,快速排序算法从数组中选择一个基准元素,将数组分成两个子数组,其中一个子数组中的元素都比基准元素小,另一个子数组中的元素都比基准元素大,然后对这两个子数组递归执行快速排序操作。

function quickSort(arr) {
  if (arr.length <= 1) return arr;
  const pivotIndex = Math.floor(arr.length / 2);
  const pivot = arr.splice(pivotIndex, 1)[0];
  const left = [];
  const right = [];
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] < pivot) {
      left.push(arr[i]);
    } else {
      right.push(arr[i]);
    }
  }
  return quickSort(left).concat([pivot], quickSort(right));
}

该函数使用递归实现快速排序操作,首先判断数组长度是否为 1 或 0,如果是则直接返回该数组,否则选择一个基准元素作为比较对象,然后将数组中小于基准元素的元素放入左子数组中,大于等于基准元素的元素放入右子数组中,最后再将左、右子数组和基准元素拼接起来。

快速排序算法的时间复杂度为 O(nlogn),是较快的排序算法之一。虽然快速排序算法在最坏情况下的时间复杂度为 O(n^2) ,但在实际应用中发生的概率较低,并且快速排序算法具有原地排序、不稳定等优点,适用于大多数无序数组排序。

归并排序算法

归并排序算法是一种基于分治思想的高效排序算法,它通过将待排序数组不断地对半切分,对每个子数组进行排序,然后再将它们合并起来。具体来说,归并排序算法将数组分成两个子数组,对这两个子数组递归执行归并排序操作,并将排好序的子数组合并起来形成一个有序数组。

function mergeSort(arr) {
  if (arr.length <= 1) return arr;
  const mid = Math.floor(arr.length / 2);
  const left = arr.slice(0, mid);
  const right = arr.slice(mid);
  return merge(mergeSort(left), mergeSort(right));
}

function merge(left, right) {
  const result = [];
  let i = 0;
  let j = 0;
  while (i < left.length && j < right.length) {
    if (left[i] < right[j]) {
      result.push(left[i]);
      i++;
    } else {
      result.push(right[j]);
      j++;
    }
  }
  return result.concat(left.slice(i), right.slice(j));
}

该函数使用递归实现归并排序操作,首先判断数组长度是否为 0 或 1,如果是则直接返回该数组,否则将数组对半切分成左、右两个子数组,并对左、右子数组分别递归执行归并排序操作,最后将排好序的左、右子数组合并起来。

归并排序算法的时间复杂度为 O(nlogn),是较快的排序算法之一。归并排序算法具有稳定、可靠的性能,并且可以应用于大规模数据集,但其空间复杂度较高,需要额外的存储空间。

案例练习 - 根据数据生成柱形图

根据数据生成柱形图 (codepen.io)

<div class="data-container">
  <h1>根据数据生成柱形图</h1>
  <label for="q1"
    >第 1 季度数据
    <input type="number" id="q1" placeholder="请输入第1季度数据" />
  </label>
  <label for="q2"
    >第 2 季度数据
    <input type="number" id="q2" placeholder="请输入第2季度数据" />
  </label>
  <label for="q3"
    >第 3 季度数据
    <input type="number" id="q3" placeholder="请输入第3季度数据" />
  </label>
  <label for="q4"
    >第 4 季度数据
    <input type="number" id="q4" placeholder="请输入第4季度数据" />
  </label>
  <button onclick="generateChart()">生成柱形图</button>
</div>
<div id="chart-container"></div>
.data-container {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  margin: 20px 0;
}

.data-container label,
.data-container button {
  margin-top: 10px;
}

.data-container input {
  margin-left: 10px;
  outline: none;
  line-height: 22px;
}

.data-container button {
  padding: 5px 10px;
  border: none;
  border-radius: 5px;
  background-color: #ccc;
  cursor: pointer;
}

.data-container button:hover {
  background-color: #aaa;
}

#chart-container {
  display: none;
  justify-content: space-evenly;
  align-items: flex-end;
  width: 800px;
  height: 400px;
  margin: 20px auto;
  /*   background-color: #ccc; */
  border-bottom: 1px solid #333;
  border-left: 1px solid #333;
}

.chart {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  width: 100px;
  height: 100%;
}

.bar {
  width: 100%;
  background-color: #45ada8;
  color: #fff;
  text-align: center;
  position: relative;
  top: 20px;
}

.label {
  width: 100%;
  height: 20px;
  /*   background-color: #eee; */
  position: relative;
  top: 20px;
}

.bar-value,
.label-value {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  text-align: center;
}

.bar-value {
  font-size: 20px;
}

.label-value {
  font-size: 13px;
}
function generateChart() {
  const data = [];
  for (let i = 1; i <= 4; i++) {
    const input = document.getElementById(`q${i}`);
    data.push(parseInt(input.value) || Math.floor(Math.random() * 100) + 100);
  }

  const chartContainer = document.getElementById("chart-container");
  chartContainer.innerHTML = ""; // 清空容器
  for (let i = 0; i < data.length; i++) {
    const bar = document.createElement("div");
    bar.classList.add("bar");
    bar.style.height = `${data[i]}px`;
    bar.innerHTML = `<div class="bar-value">${data[i]}</div>`;

    const label = document.createElement("div");
    label.classList.add("label");
    label.innerHTML = `<div class="label-value">第${i + 1}季度</div>`;

    const chart = document.createElement("div");
    chart.classList.add("chart");
    chart.appendChild(bar);
    chart.appendChild(label);

    chartContainer.appendChild(chart);
    chartContainer.style.display = "flex";
  }
}